#include <cstdlib>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <fstream>
#include "xstring.h"
#include "xstdio.h"
#include "xstdlib.h"
#include "xzlib.h"
#include "bio.h"
#include "xfaidx.h"

using namespace std;


IndexedGenome::IndexedGenome(const char* fasta):_total_size(0UL),file(NULL)
    {
    string faidx(fasta);
    char* line=NULL;
    faidx.append(".fai");
    /* open *.fai file */
    FILE* in=safeFOpen(faidx.c_str(),"r");
    /* read indexes in fai file */
    while((line=readLine(in,NULL))!=NULL)
	    {
	    if(line[0]==0)
		{
		free(line);
		continue;
		}

	    char* tab=(char*)strchr(line,'\t');
	    if(tab==NULL) { free(line);continue;}
	    *tab=0;
	    ++tab;

	    std::string chrom;
	    chrom.assign(line);

	    FaidxPtr index=new faidx2_t;
	    index->name=safeStrdup(chrom.c_str());
	    if(sscanf(tab,"%lld\t%lld\t%d\t%d",
		    &index->len, &index->offset, &index->line_blen,&index->line_len
		    )!=4)
		    {
		    cerr << "Cannot read index "<< faidx << " in "<< tab << endl;
		    exit(EXIT_FAILURE);
		    }
	    /* insert in the map(chrom,faidx) */
	    name2index.insert(make_pair(chrom,index));
	    _total_size+= index->len;
	    free(line);
	    }
    /* close index file */
   fclose(in);

   for(std::map<std::string,FaidxPtr>::iterator r=name2index.begin();
	   r!=name2index.end();
	   ++r)
       {
       r->second->tid=tid2index.size();
       tid2index.push_back(r->second);
       }
   this->file=new TypedFile<char>(fasta);
    }
/* destructor */
IndexedGenome::~IndexedGenome()
	{
	std::map<std::string,FaidxPtr>::iterator r=name2index.begin();
        std::map<std::string,FaidxPtr>::iterator r_end=name2index.end();
        while(r!=r_end)
               {
	       free(r->second->name);
               delete r->second;
               ++r;
               }
	if(file!=NULL) delete file;
	}

/* return the base at position 'index' for the chromosome indexed by faidx */
char IndexedGenome::at(const FaidxPtr faidx,int64_t index)
	{
        if(index> faidx->len) return '\0';

	long pos= faidx->offset +
		index / faidx->line_blen * faidx->line_len +
		index % faidx->line_blen
		;
	char c;
	file->get(pos,&c);
	return c;
	}


int IndexedGenome::size() const
    {
    return (int)tid2index.size();
    }


unsigned long IndexedGenome::genome_size() const
    {
    return _total_size;
    }


const FaidxPtr IndexedGenome::getFaidByTid(tid_t tid) const
    {
    return tid2index.at(tid);
    }


const FaidxPtr IndexedGenome::getFaidByName(const char* seq) const
    {
    std::map<std::string,FaidxPtr>::const_iterator r= name2index.find(seq);
    if(r==name2index.end()) return NULL;
    return r->second;
    }


/****************************************************************************/
/****************************************************************************/
/****************************************************************************/
/****************************************************************************/


ChromosomeString::ChromosomeString(IndexedGenome* genome,FaidxPtr faidx):
    _genome(genome),_faidx(faidx)
    {
    }

ChromosomeString::~ChromosomeString()
    {
    }

const char*
ChromosomeString::name() const
    {
    return _faidx->name;
    }
ChromosomeString::size_type
ChromosomeString::size() const
    {
    return _faidx->len;
    }

char
ChromosomeString::at(ChromosomeString::size_type idx) const
    {
    return ::normalize_base(_genome->at(_faidx,idx));
    }


